from rest_framework import serializers

from django.db import transaction

from apps.goods.models import SKU, GoodsCategory, SPU, SPUSpecification, SpecificationOption, SKUSpecification

from celery_tasks.html.tasks import generate_static_sku_detail_html


class SKUSpecificationSerializer(serializers.ModelSerializer):

    spec_id = serializers.IntegerField()
    option_id = serializers.IntegerField()

    class Meta:
        model = SKUSpecification  # 关联了sku表
        fields = ['spec_id', 'option_id']


class SKUSerializer(serializers.ModelSerializer):
    """sku展示页面数据序列化器"""
    # 展示数据时展示对应的名称而不是id，（spu/商品类别）,新增数据时必须传入spu和category，所以要序列化
    spu = serializers.StringRelatedField(read_only=True)
    category = serializers.StringRelatedField(read_only=True)

    # (1048, "Column 'spu_id' cannot be null")
    spu_id = serializers.IntegerField()
    category_id = serializers.IntegerField()
    # 关联嵌套序列化器（一对多）
    specs = SKUSpecificationSerializer(many=True)

    class Meta:
        model = SKU
        fields = '__all__'

    # 重写create方法，为了可以保存关联表的数据，规格和规格选项
    def create(self, validated_data):
        # 1.把validated_data中的specs数据删除并缓存起来
        specs_value = validated_data.pop('specs')
        with transaction.atomic():
            # 创建保存点
            savepoint = transaction.savepoint()
            try:
                # 2.按照正常的保存方式把不包含specs的数据保存到sku表中
                sku = SKU.objects.create(**validated_data)
                # 3.把specs数据保存到SKUSpecification(sku具体规格表)
                for item in specs_value:
                    SKUSpecification.objects.create(
                        sku=sku,
                        spec_id=item.get('spec_id'),
                        option_id=item.get('option_id')
                    )
                sku.default_image = 'group1/M00/00/02/CtM3BVrPB4GAWkTlAAGuN6wB9fU4220429'
                sku.save()
            except Exception as e:
                # 捕获异常
                transaction.savepoint_rollback(savepoint)
                return serializers.ValidationError('数据库错误')
            else:
                # 提交从保存点到当前状态的所有数据库事务操作
                transaction.savepoint_commit(savepoint)
                # 生成静态详情页面
                generate_static_sku_detail_html.delay(sku.id)

                return sku

    def update(self, instance, validated_data):
        # 1.删除validate_data中的specs，并缓存
        specs_value = validated_data.pop('specs')
        with transaction.atomic():
            """以下部分代码会在事物中执行"""
            # 创建保存点
            savepoint = transaction.savepoint()
            try:
                # 2.更新删除specs后的数据
                super().update(instance, validated_data)
                # 3.更新SKUSpecification具体规格表数据specs
                for item in specs_value:
                    SKUSpecification.objects.filter(
                        sku=instance,
                        spec_id=item.get('spec_id')
                    ).update(
                        option_id=item.get('option_id')
                    )
            except Exception as e:
                # 事物回滚
                transaction.savepoint_rollback(savepoint)
                return serializers.ValidationError('数据库错误')
            else:
                # 提交从保存点到当前状态的所有数据库事务操作
                transaction.savepoint_commit(savepoint)

                return instance


class SKUCategoriesSerializer(serializers.ModelSerializer):
    """三级分类序列化器"""
    class Meta:
        model = GoodsCategory
        fields = ['id', 'name']


class SPUSimpleSerializer(serializers.ModelSerializer):
    """SPU名称序列化器"""
    class Meta:
        model = SPU
        fields = ['id', 'name']


class SPUOptionSerializer(serializers.ModelSerializer):
    """规格选项序列化器"""
    class Meta:
        model = SpecificationOption
        fields = ['id', 'value']


class SPUSpecSerializer(serializers.ModelSerializer):
    """商品规格和规格选项序列化器"""
    spu = serializers.StringRelatedField(read_only=True)

    spu_id = serializers.IntegerField(read_only=True)
    # 关联规格选项，一对多
    options = SPUOptionSerializer(many=True)

    class Meta:
        model = SPUSpecification
        fields = '__all__'
